import Tablero
import Othello
import HUnit
import Maybe
import Char

-- evaluar t para correr todos los tests
t = runTestTT allTests

allTests = test [ 
  "triviales" ~: testsTriviales,
  "tablero" ~: testsTablero,
  "minimax" ~: testsMinimax,
  "jugar" ~: testsJugar,
  "podar" ~: testsPodar,
  "basicas" ~: tableroBasicas
  ]

testsTriviales = test [
  1 ~=? 1,
  2 ~=? 2,
  [1,2] ~=? [1,2],
  [1,2] ~~? [2,1]
  ]
  
testsTablero = test [
  _N ~=? fromJust (contenido d_4 tableroInicial),
  _N ~=? fromJust (contenido e_5 tableroInicial),
  _B ~=? fromJust (contenido e_4 tableroInicial),
  _B ~=? fromJust (contenido d_5 tableroInicial),
  Nothing ~=? contenido a_1 tableroInicial
  ]

-- idem ~=? pero sin importar el orden
(~~?) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Test
expected ~~? actual = (sort expected) ~=? (sort actual)
  where
    sort = foldl (\r e -> push r e) []
    push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

(~~) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Bool
expected ~~ actual = (sort expected) == (sort actual)
  where
    sort = foldl (\r e -> push r e) []
    push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

-- Tests propios

testsJugar = test [
    True ~=? isJust (jugar (M ('e', 3)) (J Negro juegoPrueba4)),
    True ~=? (tableroActual.fromJust)(jugar (M ('e', 3)) (J Negro juegoPrueba4)) == juegoPrueba5,
    True ~=? (jugadorActual.fromJust)(jugar (M ('e', 3)) (J Negro juegoPrueba4)) == Blanco,
    2    ~=? (length.jugadasPosibles) (J Negro juegoPrueba4)
    ]

arbolInf :: Arbol Int
arbolInf = Nodo 0 [arbolInf, arbolInf]

altura :: Arbol a -> Int
altura (Nodo _ hijos) = 1 + (foldr (max . altura) 0 hijos)

tableroBasicas = test [
    tableroVacio ~=? vacio,
    tableroBasico ~=? tableroInicial,
    contenido ('a',1) tableroInicial ~=? Nothing,
    contenido ('d',5) tableroInicial ~=? Just Blanco,
    contenido ('d',4) tableroInicial ~=? Just Negro,
    contenido ('a',1) (poner ('a',1) Blanco  tableroInicial) ~=? Just Blanco,
    contenido ('a',2) (poner ('a',1) Blanco  tableroInicial) ~=? Nothing,
    contenido ('d',4) (poner ('d',4) Blanco  tableroInicial) ~=? Just Blanco,
    desplazarFila 1 ('c',2) ~=? ('c',3),
    desplazarColumna 1 ('c',2) ~=? ('d',2),
    desplazar 1 1 ('c',2) ~=?('d',3),
    generar ('a',5) (desplazar 0 1) ~=? [('a',5),('a',6),('a',7),('a',8)],
    generar ('f',5) (desplazar 1 0) ~=? [('f',5),('g',5),('h',5)],
    generar ('f',5) (desplazar 1 1) ~=? [('f',5),('g',6),('h',7)],
    posicionesAInvertir ('d',6) (poner ('d',6) Negro tableroInicial) ~=? [('d',5)],
    posicionesAInvertir ('e',4) (poner ('e',4) Blanco juegoPrueba1) ~=? [('c',4),('d',4)],
    contenido ('c',4) (invertirTodas (posicionesAInvertir ('e',4) (poner ('e',4) Blanco juegoPrueba1)) (poner ('e',4) Blanco juegoPrueba1)) ~=? Just Blanco
    ]


testsPodar = test [
    1 ~=? altura (podar 1 arbolInf),
    2 ~=? altura (podar 2 arbolInf),
    10 ~=? altura (podar 10 arbolInf)
    ]


testsMinimax = test [
  
  (  0.875, [M ('e', 6), M ('d', 6), Paso]) ~=? minimax valuacionBasica (podar 4 (arbolDeJugadas (J Negro  juegoPrueba1))),
  (  0.875, [M ('e', 6), M ('d', 6)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Negro  juegoPrueba1))),
  
  ( -0.875, [M ('e', 6), M ('d', 6), Paso]) ~=? minimax valuacionBasica (podar 4 (arbolDeJugadas (J Blanco juegoPrueba1))),
  ( -0.875, [M ('e', 6), M ('d', 6)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Blanco juegoPrueba1))),
  
  (  0.875, [M ('d', 6), M ('e', 6), Paso]) ~=? minimax valuacionBasica (podar 4 (arbolDeJugadas (J Negro  juegoPrueba2))),
  (  0.875, [M ('d', 6), M ('e', 6)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Negro  juegoPrueba2))),
  
  ( -0.875, [M ('d', 6), M ('e', 6), Paso]) ~=? minimax valuacionBasica (podar 4 (arbolDeJugadas (J Blanco juegoPrueba2))),
  ( -0.875, [M ('d', 6), M ('e', 6)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Blanco juegoPrueba2))),
  
  ( 0.8125, [M ('e', 5), M ('d', 5)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Negro  juegoPrueba3))),
  (-0.8125, [M ('e', 5), M ('d', 5)      ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Blanco juegoPrueba3))),
  
  ( 1.0000, [M ('d', 5)                  ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Negro  juegoPrueba4))),
  (-1.0000, [Paso, M ('d', 5)            ]) ~=? minimax valuacionBasica (podar 3 (arbolDeJugadas (J Blanco juegoPrueba4)))
  
  ]

instance Eq Tablero where
  (==) = tableroEquals

tableroEquals :: Tablero -> Tablero -> Bool
tableroEquals t1 t2 = and [contenido pos t1 == contenido pos t2 | pos <- todasLasPosiciones]

fromString :: [Char] -> Tablero
fromString str = tableroFromString str 0 vacio

tableroFromString :: [Char] -> Int -> Tablero -> Tablero
tableroFromString []       n t   = t
tableroFromString ('N':cs) n t = poner (numToPosicion n) Negro  (tableroFromString cs (n + 1) t)
tableroFromString ('B':cs) n t = poner (numToPosicion n) Blanco (tableroFromString cs (n + 1) t)
tableroFromString (' ':cs) n t = tableroFromString cs (n + 1) t

numToPosicion :: Int -> Posicion
numToPosicion n = (chr(97 + n `mod` 8), 8 - (n `div` 8))


{-
  Juego de prueba 1.
  A cualquiera de ambos jugadores les conviene jugar primero en e6 (a la derecha),
  pues esto permite al Blanco conquistar una diagonal más larga y al Negro evitar
  que el Blanco haga esto en la siguiente movida.
-}
tableroVacio :: Tablero
tableroVacio = fromString ("        "
                        ++ "        "
                        ++ "        "
                        ++ "        "
                        ++ "        "
                        ++ "        "
                        ++ "        "
                        ++ "        " )

tableroBasico :: Tablero
tableroBasico = fromString ("        "
                        ++  "        "
                        ++  "        "
                        ++  "   BN   "
                        ++  "   NB   "
                        ++  "        "
                        ++  "        "
                        ++  "        " )


{-
  Juego de prueba 1.
  A cualquiera de ambos jugadores les conviene jugar primero en e6 (a la derecha),
  pues esto permite al Blanco conquistar una diagonal más larga y al Negro evitar
  que el Blanco haga esto en la siguiente movida.
-}
juegoPrueba1 :: Tablero
juegoPrueba1 = fromString ("NNNNNNNN"
                        ++ "NNNBBNNN"
                        ++ "NNN  NNN"
                        ++ "NNNNNNNN"
                        ++ "NBNNNNNN"
                        ++ "NNNNNNNB"
                        ++ "NNNNNNNN"
                        ++ "NNNNNNNN" )

{-
  Juego de prueba 2.
  Es análogo al anterior pero reflejado horizontalmente.
  A ambos les conviene jugar a la izquierda.
-}
juegoPrueba2 :: Tablero
juegoPrueba2 = fromString ("NNNNNNNN"
                        ++ "NNNBBNNN"
                        ++ "NNN  NNN"
                        ++ "NNNNNNNN"
                        ++ "NNNNNNBN"
                        ++ "BNNNNNNN"
                        ++ "NNNNNNNN"
                        ++ "NNNNNNNN" )

{-
  Juego de prueba 3.
  Parecido al 2, pero ahora conviene jugar a la derecha porque se
  agrega una ficha blanca abajo.
-}
juegoPrueba3 :: Tablero
juegoPrueba3 = fromString ("NNNNNNNN"
                        ++ "NNNNNNNN"
                        ++ "NNNBBNNN"
                        ++ "NNN  NNN"
                        ++ "NNNNNNNN"
                        ++ "NNNNNNBN"
                        ++ "BNNNNNNN"
                        ++ "NNNNBNNN" )

{-
  Juego de prueba 4.
  Al Negro le conviene jugar arriba ('d', 5) porque así da vuelta todas
  las blancas. El Blanco debe pasar.
-}
juegoPrueba4 :: Tablero
juegoPrueba4 = fromString ("NNNNNNNN"
                        ++ "NBNBNBNN"
                        ++ "NNBBBNNN"
                        ++ "NBB BBNN"
                        ++ "NNBBBNNN"
                        ++ "NBNB BNN"
                        ++ "NNNNNNBN"
                        ++ "NNNNNNNN" )

{-
  Juego de prueba 5.
  Es el resultado de que el Negro juegue mal en el juegoPrueba4.
-}
juegoPrueba5 :: Tablero
juegoPrueba5 = fromString ("NNNNNNNN"
                        ++ "NBNBNBNN"
                        ++ "NNBBNNNN"
                        ++ "NBN NBNN"
                        ++ "NNBNNNNN"
                        ++ "NBNNNNNN"
                        ++ "NNNNNNBN"
                        ++ "NNNNNNNN" )


-- constantes para que los tests sean más legibles
_N = Negro
_B = Blanco
a_1 = ('a',1::Int)
b_1 = ('b',1::Int)
c_1 = ('c',1::Int)
d_1 = ('d',1::Int)
e_1 = ('e',1::Int)
f_1 = ('f',1::Int)
g_1 = ('g',1::Int)
h_1 = ('h',1::Int)

a_2 = ('a',2::Int)
b_2 = ('b',2::Int)
c_2 = ('c',2::Int)
d_2 = ('d',2::Int)
e_2 = ('e',2::Int)
f_2 = ('f',2::Int)
g_2 = ('g',2::Int)
h_2 = ('h',2::Int)

a_3 = ('a',3::Int)
b_3 = ('b',3::Int)
c_3 = ('c',3::Int)
d_3 = ('d',3::Int)
e_3 = ('e',3::Int)
f_3 = ('f',3::Int)
g_3 = ('g',3::Int)
h_3 = ('h',3::Int)

a_4 = ('a',4::Int)
b_4 = ('b',4::Int)
c_4 = ('c',4::Int)
d_4 = ('d',4::Int)
e_4 = ('e',4::Int)
f_4 = ('f',4::Int)
g_4 = ('g',4::Int)
h_4 = ('h',4::Int)

a_5 = ('a',5::Int)
b_5 = ('b',5::Int)
c_5 = ('c',5::Int)
d_5 = ('d',5::Int)
e_5 = ('e',5::Int)
f_5 = ('f',5::Int)
g_5 = ('g',5::Int)
h_5 = ('h',5::Int)

a_6 = ('a',6::Int)
b_6 = ('b',6::Int)
c_6 = ('c',6::Int)
d_6 = ('d',6::Int)
e_6 = ('e',6::Int)
f_6 = ('f',6::Int)
g_6 = ('g',6::Int)
h_6 = ('h',6::Int)

a_7 = ('a',7::Int)
b_7 = ('b',7::Int)
c_7 = ('c',7::Int)
d_7 = ('d',7::Int)
e_7 = ('e',7::Int)
f_7 = ('f',7::Int)
g_7 = ('g',7::Int)
h_7 = ('h',7::Int)

a_8 = ('a',8::Int)
b_8 = ('b',8::Int)
c_8 = ('c',8::Int)
d_8 = ('d',8::Int)
e_8 = ('e',8::Int)
f_8 = ('f',8::Int)
g_8 = ('g',8::Int)
h_8 = ('h',8::Int)
